package com.szeldon.declatest.exec;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import com.szeldon.declatest.declaration.TestsSuiteDeclaration;
import com.szeldon.declatest.eval.IScenarioEvaluatorPlugin;
import com.szeldon.declatest.eval.ScenarioPartEvaluator;
import com.szeldon.declatest.exec.plugins.IDoScenarioStepExecutorPlugin;
import com.szeldon.declatest.reports.TestSuiteReportData;

/**
 * Superclass for classes responsible for executing test suites.
 * 
 * @author szeldon
 * 
 */
public abstract class AbstractTestsSuiteExecutor {

    /** Lock of this object. */
    private Object lock = new Object();

    /** Current state of this executor. */
    protected TestsSuitesExecutorState state = TestsSuitesExecutorState.IDLE;

    /** List of declarations of test suites. */
    protected List<TestsSuiteDeclaration> suitesList = new ArrayList<TestsSuiteDeclaration>();

    /** Evaluator. */
    protected ScenarioPartEvaluator evaluator = new ScenarioPartEvaluator();

    /** List of containers for history of test suites. */
    protected List<TestSuiteExecHistory> historiesList = new ArrayList<TestSuiteExecHistory>();

    /** Map where key is a type of endpoint and value is an executor for that type. */
    protected Map<String, IDoScenarioStepExecutorPlugin> doScenarioPartExecutors = new HashMap<String, IDoScenarioStepExecutorPlugin>();

    /** Listener of this executor. */
    protected ITestSuiteExecutorListener listener;

    /** Properties from property files for test suite. */
    protected Properties properties;

    /**
     * Adds given testsSuite to this executor. Prevents from adding during execution of tests suite (if state is
     * {@link TestsSuitesExecutorState#IN_PROGRESS}, this method will be waiting up until a moment when execution will
     * stop.
     * 
     * @param testsSuite
     */
    public void addTestsSuiteDeclaration(TestsSuiteDeclaration testsSuite) {
        synchronized (lock) {
            suitesList.add(testsSuite);
        }
    }

    /**
     * Executes test suites added previously. This method gets the lock of this object and gives execution time to
     * {@link #abstractExecute()} method. If another thread will try to invoke this method, it'll wait for the previous
     * one to end.
     * 
     */
    public final List<TestSuiteReportData> execute() {
        List<TestSuiteReportData> reportsList = null;
        synchronized (lock) {
            state = TestsSuitesExecutorState.IN_PROGRESS;
            reportsList = abstractExecute();
        }
        state = TestsSuitesExecutorState.IDLE;

        return reportsList;
    }

    /**
     * Sets given listener as a listener of events of this executor.
     */
    public void setListener(ITestSuiteExecutorListener newListener) {
        synchronized (lock) {
            this.listener = newListener;
        }
    }

    /**
     * Method executed by {@link #execute()} method. To be implemented with code of executor.
     * 
     */
    protected abstract List<TestSuiteReportData> abstractExecute();

    /**
     * Adds plugin to evaluator used by this executor. If executor is running (
     * {@link TestsSuitesExecutorState#IN_PROGRESS} state), plugin will be added after that.
     * 
     * @param plugin
     */
    public void addEvaluatorPlugin(IScenarioEvaluatorPlugin plugin) {
        synchronized (lock) {
            evaluator.addPlugin(plugin);
        }
    }

    /**
     * Adds given plugin. It will be used for executing DoScenarioStep part. If executor is running (
     * {@link TestsSuitesExecutorState#IN_PROGRESS} state), plugin will be added after that.
     * 
     * @param plugin
     */
    public void addDoScenarioPartExecutorPlugin(IDoScenarioStepExecutorPlugin plugin) {
        synchronized (lock) {
            doScenarioPartExecutors.put(plugin.getSupportedEndpointType(), plugin);
        }
    }

    public List<TestSuiteExecHistory> getHistoriesList() {
        return historiesList;
    }

    /**
     * Returns properties got from property files for test suite.
     * 
     * @return
     */
    public Properties getProperties() {
        return properties;
    }

}